AutoTel Unboxing

AutoTel Analysis (Work-in-Progress)

TODO:

  1. Geocode adresses using Google Maps API - DONE
  2. Calculate distances using Mapbox Direction API - Started (Not good, maybe try Google Directions API)
  3. Find too short distances (depends on latter)
  4. Geocode designated parking spaces and mark on map
  5. Create holoviews map with date and time sliders, distance and minute show somehow

BIG TODO: somehow host or embed HoloViews+Bokeh dynamic maps

In [2]:
import numpy as np
import pandas as pd
import holoviews as hv
import qgrid
import seaborn as sns
#qgrid.enable()

%matplotlib inline

hv.extension('bokeh', 'matplotlib', width="100")
%opts Curve [width=600 height=250 tools=['hover'] ] {+framewise} VLine (color="black")
%opts Bars  [width=800 height=400 tools=['hover'] group_index=1 legend_position='top_left' xrotation=90]
In [3]:
df = pd.read_excel('data/ניתוח נסיעות.xlsx', sheet_name='Sheet1', skiprows=2, usecols=8, parse_dates=True,
                  names = ['vehicle_id', 'order_id', 'shiryun_cat', 'avg_dist', 
                           'billing_minutes', 'orig_addr', 'dest_addr', 'start_dt', 'end_dt'])
C:\Users\dbare\Anaconda3\envs\pyviz\lib\site-packages\pandas\io\excel.py:380: UserWarning: The 'parse_dates=True' keyword of read_excel was provided without an 'index_col' keyword value.
  warn("The 'parse_dates=True' keyword of read_excel was provided"
In [4]:
df = (df[~df.shiryun_cat.isin(['Canceled', 'NO SHOW', 'Self Service'])]
      .assign(kmh = lambda x: x.avg_dist/(x.billing_minutes/60))
     )
In [ ]:
df.sort_values(by='kmh', ascending=False).head(10)
In [6]:
df[df.kmh>60].shape
Out[6]:
(501, 10)
In [ ]:
df[df.kmh<0.05].head()
In [8]:
df.set_index('start_dt').resample('D')[['billing_minutes', 'avg_dist']].sum().plot(figsize=[10,7], title='Daily sums')
Out[8]:
<matplotlib.axes._subplots.AxesSubplot at 0x2e8b26f6d68>
In [9]:
df.set_index('start_dt').resample('D')[['billing_minutes', 'avg_dist']].mean().plot(figsize=[10,7], title='Daily means')
Out[9]:
<matplotlib.axes._subplots.AxesSubplot at 0x2e8b24ff550>
In [10]:
size = df[(df.billing_minutes<100) & (df.avg_dist<100) & (df.kmh<50) & (df.kmh>0)].set_index('start_dt').resample('D').size()
size.plot(figsize=[10,7], title='Daily trip count')
Out[10]:
<matplotlib.axes._subplots.AxesSubplot at 0x2e8b1a70ef0>
In [11]:
ewm7 = df[(df.billing_minutes<100) & (df.avg_dist<100) & (df.kmh<50) & (df.kmh>0)].set_index('start_dt').resample('D').size().ewm(span=7).mean()
ewm30 = df[(df.billing_minutes<100) & (df.avg_dist<100) & (df.kmh<50) & (df.kmh>0)].set_index('start_dt').resample('D').size().ewm(span=30).mean()
pd.concat([size, ewm7, ewm30], axis=1, keys=['Daily count', '7 day avg', '30 day avg']).plot(figsize=[10,7], title='Daily trip count (with rolling window averages)')
Out[11]:
<matplotlib.axes._subplots.AxesSubplot at 0x2e8b766cfd0>

Too short

In [12]:
under_1min = df[df.billing_minutes<=1].set_index('start_dt').resample('D').size()
under_1km = df[df.avg_dist<=1].set_index('start_dt').resample('D').size()


pd.concat([under_1min, under_1km], axis=1, keys=['Under 1 minute', 'Under 1 km']).plot(figsize=[10,7], title='Too short')
Out[12]:
<matplotlib.axes._subplots.AxesSubplot at 0x2e8b1835fd0>

Too long

In [13]:
df.plot(kind='scatter', x='billing_minutes', y='avg_dist', figsize=[10,7])
Out[13]:
<matplotlib.axes._subplots.AxesSubplot at 0x2e8b19226a0>
In [14]:
import altair as alt

df[(df.billing_minutes<300) & (df.avg_dist<300)].plot(kind='scatter', x='billing_minutes', y='avg_dist', figsize=[10,7],)
Out[14]:
<matplotlib.axes._subplots.AxesSubplot at 0x2e8b1cc2160>

Categorize by minutes and kms

  1. bin using .qcut() and then plot
  2. make a heatmap

Daily means

In [15]:
ax = df[(df.billing_minutes<200) & (df.avg_dist<200)].set_index('start_dt').resample('D')[['billing_minutes', 'avg_dist']].mean().ewm(span=7).mean().plot(figsize=[10,7], title='Daily means')

ax.get_figure().savefig('means.png')

Medians

In [16]:
ax = df[(df.billing_minutes<200) & (df.avg_dist<200)].set_index('start_dt').resample('D')[['billing_minutes', 'avg_dist']].median().ewm(span=7).mean().plot(figsize=[10,7], title='Daily medians (smoothed)')

ax.get_figure().savefig('medians.png')

Monthly boxplots

In [17]:
import seaborn as sns
import matplotlib.pyplot as plt

f, ax = plt.subplots(figsize=(10,7))
dfm = df[(df.billing_minutes<100) & (df.avg_dist<100) & (df.kmh<50)].assign(month=lambda x: x.start_dt.dt.strftime('%b')).set_index('start_dt').resample('D')

sns.boxplot(x = dfm.month.min(), y=dfm.avg_dist.mean(), ax=ax)
Out[17]:
<matplotlib.axes._subplots.AxesSubplot at 0x2e8b20a2ba8>
In [18]:
f, ax = plt.subplots(figsize=(10,7))

sns.boxplot(x = dfm.month.min(), y=dfm.billing_minutes.mean(), ax=ax)
f.savefig('month_minutes_boxplot_mean.png')
In [19]:
f, ax = plt.subplots(figsize=(10,7))

sns.boxplot(x = dfm.month.min(), y=dfm.billing_minutes.median(), ax=ax)
f.savefig('month_minutes_boxplot_median.png')

Geo

Start by cleaning up the addresses

In [20]:
df['orig_addr_c'] = df['orig_addr'].str.replace(r'(\d+)-(\d+)', r'\1').str.replace(r'\\', r'')
df['dest_addr_c'] = df['dest_addr'].str.replace(r'(\d+)-(\d+)', r'\1').str.replace(r'\\', r'')
In [21]:
df[['orig_addr', 'orig_addr_c']].head(10)
Out[21]:
orig_addr orig_addr_c
0 גורדון 86-88, תל אביב יפו, ישראל גורדון 86, תל אביב יפו, ישראל
1 יעקב אפטר 3, תל אביב יפו, ישראל יעקב אפטר 3, תל אביב יפו, ישראל
2 רופין 32-34, תל אביב יפו, ישראל רופין 32, תל אביב יפו, ישראל
3 שדרות מסריק 19, תל אביב יפו, ישראל שדרות מסריק 19, תל אביב יפו, ישראל
4 הירקון 188, תל אביב יפו, ישראל הירקון 188, תל אביב יפו, ישראל
5 רופין 32-34, תל אביב יפו, ישראל רופין 32, תל אביב יפו, ישראל
6 יוחנן הסנדלר 13, תל אביב יפו, ישראל יוחנן הסנדלר 13, תל אביב יפו, ישראל
7 שפ\"ר 9, תל אביב יפו, ישראל שפ"ר 9, תל אביב יפו, ישראל
8 אבא אחימאיר 16-22, תל אביב יפו, ישראל אבא אחימאיר 16, תל אביב יפו, ישראל
9 מגדל בנק דיסקונט, תל אביב יפו, ישראל מגדל בנק דיסקונט, תל אביב יפו, ישראל
In [22]:
origs = pd.concat([df['orig_addr_c'], df['dest_addr_c']]).unique()
origs.shape
Out[22]:
(13345,)
In [23]:
pd.Series(origs[:10])
Out[23]:
0           גורדון 86, תל אביב יפו, ישראל
1         יעקב אפטר 3, תל אביב יפו, ישראל
2            רופין 32, תל אביב יפו, ישראל
3      שדרות מסריק 19, תל אביב יפו, ישראל
4          הירקון 188, תל אביב יפו, ישראל
5     יוחנן הסנדלר 13, תל אביב יפו, ישראל
6              שפ"ר 9, תל אביב יפו, ישראל
7      אבא אחימאיר 16, תל אביב יפו, ישראל
8    מגדל בנק דיסקונט, תל אביב יפו, ישראל
9          דרך הגבורה, תל אביב יפו, ישראל
dtype: object
In [24]:
import googlemaps

API_KEY = 'AIzaSyDVUvTvTqRCbZH6lhMl6VX3GQ_A5ucPKhc'
gmaps = googlemaps.Client(key=API_KEY)

geocode_result  = gmaps.geocode('גורדון 86, תל אביב יפו, ישראל')

geocode_result
Out[24]:
[{'address_components': [{'long_name': '86',
    'short_name': '86',
    'types': ['street_number']},
   {'long_name': 'J. L. Gordon Street',
    'short_name': 'J. L. Gordon St',
    'types': ['route']},
   {'long_name': 'Tel Aviv-Yafo',
    'short_name': 'Tel Aviv-Yafo',
    'types': ['locality', 'political']},
   {'long_name': 'Tel Aviv District',
    'short_name': 'Tel Aviv District',
    'types': ['administrative_area_level_1', 'political']},
   {'long_name': 'Israel',
    'short_name': 'IL',
    'types': ['country', 'political']}],
  'formatted_address': 'J. L. Gordon St 86, Tel Aviv-Yafo, Israel',
  'geometry': {'location': {'lat': 32.0807428, 'lng': 34.7792228},
   'location_type': 'ROOFTOP',
   'viewport': {'northeast': {'lat': 32.0820917802915,
     'lng': 34.7805717802915},
    'southwest': {'lat': 32.0793938197085, 'lng': 34.7778738197085}}},
  'place_id': 'ChIJ84UPDIZLHRURIXBQ2R8A4bU',
  'types': ['street_address']}]
#d = dict(zip(origs, pd.Series(origs).apply(gmaps.geocode).apply(lambda x: (x[0]['geometry']['location']['lat'], x[0]['geometry']['location']['lng'])))) #d
In [25]:
import pickle
#pickle.dump(d, open('data/coordinates_dict.pkl', 'wb'))
with open('data/coordinates_dict.pkl', 'rb') as f:
    d = pickle.load(f)
In [26]:
df['orig_coord'] = df['orig_addr_c'].map(d)
df['dest_coord'] = df['dest_addr_c'].map(d)
In [27]:
df.head(10)
Out[27]:
vehicle_id order_id shiryun_cat avg_dist billing_minutes orig_addr dest_addr start_dt end_dt kmh orig_addr_c dest_addr_c orig_coord dest_coord
0 46-525-81 60386 שריון חינמי 8 12.92 גורדון 86-88, תל אביב יפו, ישראל הרב רובינשטיין 61, תל אביב יפו, ישראל 2017-10-23 00:08:53 2017-10-23 00:31:40.370 37.151703 גורדון 86, תל אביב יפו, ישראל הרב רובינשטיין 61, תל אביב יפו, ישראל (32.0807428, 34.7792228) (32.0357011, 34.7581719)
1 48-437-81 60387 שריון חינמי 3 8.35 יעקב אפטר 3, תל אביב יפו, ישראל ז'בוטינסקי 10, תל אביב יפו, ישראל 2017-10-23 00:09:14 2017-10-23 00:21:42.180 21.556886 יעקב אפטר 3, תל אביב יפו, ישראל ז'בוטינסקי 10, תל אביב יפו, ישראל (32.1050698, 34.7892274) (32.0895831, 34.7734306)
2 63-887-81 60388 שריון חינמי 0 1.62 רופין 32-34, תל אביב יפו, ישראל רופין 32-34, תל אביב יפו, ישראל 2017-10-23 00:10:00 2017-10-23 00:15:23.940 0.000000 רופין 32, תל אביב יפו, ישראל רופין 32, תל אביב יפו, ישראל (32.0836176, 34.7710507) (32.0836176, 34.7710507)
3 63-906-81 60389 שריון חינמי 4 9.15 שדרות מסריק 19, תל אביב יפו, ישראל אונקלוס 3, תל אביב יפו, ישראל 2017-10-23 00:12:25 2017-10-23 00:23:28.590 26.229508 שדרות מסריק 19, תל אביב יפו, ישראל אונקלוס 3, תל אביב יפו, ישראל (32.0792292, 34.77892540000001) (32.0957684, 34.7956908)
4 60-589-55 60390 שריון חינמי 2 6.43 הירקון 188, תל אביב יפו, ישראל גורדון 83-85, תל אביב יפו, ישראל 2017-10-23 00:12:55 2017-10-23 00:21:58.113 18.662519 הירקון 188, תל אביב יפו, ישראל גורדון 83, תל אביב יפו, ישראל (32.0874575, 34.7714172) (32.0807886, 34.7793035)
5 63-887-81 60391 שריון חינמי 4 12.35 רופין 32-34, תל אביב יפו, ישראל רציף העלייה השניה 2-8, תל אביב יפו, ישראל 2017-10-23 00:15:48 2017-10-23 00:28:45.000 19.433198 רופין 32, תל אביב יפו, ישראל רציף העלייה השניה 2, תל אביב יפו, ישראל (32.0836176, 34.7710507) (32.05569380000001, 34.7560594)
6 60-588-55 60392 שריון חינמי 4 7.18 יוחנן הסנדלר 13, תל אביב יפו, ישראל דרך הגבורה, תל אביב יפו, ישראל 2017-10-23 00:26:36 2017-10-23 00:41:54.770 33.426184 יוחנן הסנדלר 13, תל אביב יפו, ישראל דרך הגבורה, תל אביב יפו, ישראל (32.0695394, 34.7733708) (32.0659057, 34.8009158)
7 46-523-81 60393 שריון חינמי 0 13.10 שפ\"ר 9, תל אביב יפו, ישראל דוד תדהר 18, תל אביב יפו, ישראל 2017-10-23 00:26:39 2017-10-23 00:47:05.033 0.000000 שפ"ר 9, תל אביב יפו, ישראל דוד תדהר 18, תל אביב יפו, ישראל (32.0675208, 34.7689971) (32.0524466, 34.7864049)
8 60-604-55 60394 שריון חינמי 7 14.38 אבא אחימאיר 16-22, תל אביב יפו, ישראל נצח ישראל 6, תל אביב יפו, ישראל 2017-10-23 00:45:22 2017-10-23 01:03:40.953 29.207232 אבא אחימאיר 16, תל אביב יפו, ישראל נצח ישראל 6, תל אביב יפו, ישראל (32.1245703, 34.8018946) (32.0777774, 34.7786358)
9 71-818-55 60395 שריון חינמי 12 18.18 מגדל בנק דיסקונט, תל אביב יפו, ישראל אח\"י דקר 22, תל אביב יפו, ישראל 2017-10-23 00:48:42 2017-10-23 01:13:03.110 39.603960 מגדל בנק דיסקונט, תל אביב יפו, ישראל אח"י דקר 22, תל אביב יפו, ישראל (32.06180260000001, 34.7708857) (32.119989, 34.8390539)
In [28]:
#pickle.dump(df, open('data/df1.pkl', 'wb'))
In [29]:
df[['orig_x', 'orig_y']] = df['orig_coord'].apply(pd.Series)
df[['dest_x', 'dest_y']] = df['dest_coord'].apply(pd.Series)
In [30]:
#pickle.dump(df, open('data/df2.pkl', 'wb'))
In [31]:
df.head()
Out[31]:
vehicle_id order_id shiryun_cat avg_dist billing_minutes orig_addr dest_addr start_dt end_dt kmh orig_addr_c dest_addr_c orig_coord dest_coord orig_x orig_y dest_x dest_y
0 46-525-81 60386 שריון חינמי 8 12.92 גורדון 86-88, תל אביב יפו, ישראל הרב רובינשטיין 61, תל אביב יפו, ישראל 2017-10-23 00:08:53 2017-10-23 00:31:40.370 37.151703 גורדון 86, תל אביב יפו, ישראל הרב רובינשטיין 61, תל אביב יפו, ישראל (32.0807428, 34.7792228) (32.0357011, 34.7581719) 32.080743 34.779223 32.035701 34.758172
1 48-437-81 60387 שריון חינמי 3 8.35 יעקב אפטר 3, תל אביב יפו, ישראל ז'בוטינסקי 10, תל אביב יפו, ישראל 2017-10-23 00:09:14 2017-10-23 00:21:42.180 21.556886 יעקב אפטר 3, תל אביב יפו, ישראל ז'בוטינסקי 10, תל אביב יפו, ישראל (32.1050698, 34.7892274) (32.0895831, 34.7734306) 32.105070 34.789227 32.089583 34.773431
2 63-887-81 60388 שריון חינמי 0 1.62 רופין 32-34, תל אביב יפו, ישראל רופין 32-34, תל אביב יפו, ישראל 2017-10-23 00:10:00 2017-10-23 00:15:23.940 0.000000 רופין 32, תל אביב יפו, ישראל רופין 32, תל אביב יפו, ישראל (32.0836176, 34.7710507) (32.0836176, 34.7710507) 32.083618 34.771051 32.083618 34.771051
3 63-906-81 60389 שריון חינמי 4 9.15 שדרות מסריק 19, תל אביב יפו, ישראל אונקלוס 3, תל אביב יפו, ישראל 2017-10-23 00:12:25 2017-10-23 00:23:28.590 26.229508 שדרות מסריק 19, תל אביב יפו, ישראל אונקלוס 3, תל אביב יפו, ישראל (32.0792292, 34.77892540000001) (32.0957684, 34.7956908) 32.079229 34.778925 32.095768 34.795691
4 60-589-55 60390 שריון חינמי 2 6.43 הירקון 188, תל אביב יפו, ישראל גורדון 83-85, תל אביב יפו, ישראל 2017-10-23 00:12:55 2017-10-23 00:21:58.113 18.662519 הירקון 188, תל אביב יפו, ישראל גורדון 83, תל אביב יפו, ישראל (32.0874575, 34.7714172) (32.0807886, 34.7793035) 32.087457 34.771417 32.080789 34.779303

Geo binning

In [32]:
import geopandas as gpd
import zipfile

n = gpd.read_file('data/tlv_neighborhoods/Neighbourhoods.shp')
In [33]:
n.shape
Out[33]:
(71, 6)
In [34]:
n.head()
Out[34]:
oidshchuna msshchuna shemshchun dateimport ShapeArea geometry
0 1.0 3.0 אזור שדה דב 10/06/2015 08:36:43 1.307955e+06 POLYGON ((3872625.725060304 3779553.305307336,...
1 2.0 2.0 צוקי אביב 10/06/2015 08:36:43 1.288925e+06 POLYGON ((3873500.046031086 3780438.249958017,...
2 3.0 7.0 'רמת אביב ג 10/06/2015 08:36:43 1.101582e+06 POLYGON ((3874870.557294896 3780217.960008291,...
3 4.0 11.0 אוניברסיטת ת"א 10/06/2015 08:36:43 1.459851e+06 POLYGON ((3874180.345358506 3778818.041849776,...
4 5.0 6.0 כוכב הצפון 10/06/2015 08:36:43 4.212298e+05 POLYGON ((3872084.008575156 3776265.150082928,...
In [35]:
n[['geometry']].unary_union
Out[35]:
In [37]:
df[['orig_x', 'orig_y']].head()
Out[37]:
orig_x orig_y
0 32.080743 34.779223
1 32.105070 34.789227
2 32.083618 34.771051
3 32.079229 34.778925
4 32.087457 34.771417
from pyproj import Proj, transform inProj = Proj(init='epsg:4326') outProj = Proj(init='epsg:3857') def towgs84(row): return pd.Series(transform(inProj, outProj, row["orig_x"], row["orig_y"])) wsg84_df = df.apply(towgs84, axis=1) # new coord dataframe with two columns wsg84_df.columns = ['orig_x', 'orig_y'] wsg84_df.head()
In [38]:
def wgs84_to_web_mercator(df, lon="orig_y", lat="orig_x"):
    """Converts decimal longitude/latitude to Web Mercator format"""
    k = 6378137
    df[lat+'_merc'] = df[lon] * (k * np.pi/180.0)
    df[lon+'_merc'] = np.log(np.tan((90 + df[lat]) * np.pi/360.0)) * k 
    return df

df = df.pipe(wgs84_to_web_mercator, lon="orig_y", lat="orig_x").pipe(wgs84_to_web_mercator, lon="dest_y", lat="dest_x")
In [39]:
from geopandas import GeoDataFrame, GeoSeries
from shapely.geometry import Point

orig_geometry = [Point(xy) for xy in zip(df.orig_x_merc, df.orig_y_merc)]
orig_gs = GeoSeries(orig_geometry)
In [40]:
orig_gs.head()
Out[40]:
0    POINT (3871605.372281811 3773914.047133294)
1    POINT (3872719.079259401 3777110.587012315)
2     POINT (3870695.658271099 3774291.74821267)
3     POINT (3871572.26586525 3773715.189950116)
4    POINT (3870736.456864475 3774796.265971644)
dtype: object
In [42]:
ins = []
for i, nei in n.geometry.iteritems():
    ins.append(orig_gs.intersects(nei))
orig_point_ins = pd.concat(ins, axis=1, keys=n.shemshchun.tolist())
orig_point_ins = orig_point_ins.idxmax(axis=1)
orig_point_ins
Out[42]:
0              הצפון הישן-החלק הדרומי
1                            'תכנית ל
2              הצפון הישן-החלק הדרומי
3              הצפון הישן-החלק הדרומי
4            הצפון הישן - החלק הצפוני
5              הצפון הישן-החלק הדרומי
6                          לב תל-אביב
7                          לב תל-אביב
8                         'רמת אביב ג
9                          לב תל-אביב
10                            תל-חיים
11           הצפון הישן - החלק הצפוני
12             הצפון הישן-החלק הדרומי
13                          רמת החייל
14                         לב תל-אביב
15                           יד אליהו
16                         לב תל-אביב
17                               בבלי
18                  ביצרון ורמת ישראל
19                     אוניברסיטת ת"א
20        הצפון החדש-סביבת ככר המדינה
21                     אוניברסיטת ת"א
22                               בבלי
23                           פלורנטין
24                 נוה אביבים וסביבתה
25                 נוה אביבים וסביבתה
26           הצפון הישן - החלק הצפוני
27                            נופי ים
28                         לב תל-אביב
29                           'תכנית ל
                     ...             
136301                     לב תל-אביב
136302       הצפון הישן - החלק הצפוני
136303         הצפון הישן-החלק הדרומי
136304       הצפון הישן - החלק הצפוני
136305                         עתידים
136306       הצפון הישן - החלק הצפוני
136307         הצפון הישן-החלק הדרומי
136308                      צוקי אביב
136309                       פלורנטין
136310                     לב תל-אביב
136311                       נוה שאנן
136312                 אוניברסיטת ת"א
136313         הצפון הישן-החלק הדרומי
136314       הצפון הישן - החלק הצפוני
136315                     לב תל-אביב
136316                 אוניברסיטת ת"א
136317                       צפון יפו
136318                    פארק הירקון
136319                    פארק הירקון
136320                   צמרות איילון
136321                       צפון יפו
136322                    פארק הירקון
136323                       פלורנטין
136324         הצפון הישן-החלק הדרומי
136325                       פלורנטין
136326                     לב תל-אביב
136327                   מרכז הירידים
136328                       צפון יפו
136329       הצפון הישן - החלק הצפוני
136330         הצפון הישן-החלק הדרומי
Length: 136331, dtype: object
In [43]:
dest_geometry = [Point(xy) for xy in zip(df.dest_x_merc, df.dest_y_merc)]
dest_gs = GeoSeries(dest_geometry)
In [44]:
ins = []
for i, nei in n.geometry.iteritems():
    ins.append(dest_gs.intersects(nei))
dest_point_ins = pd.concat(ins, axis=1, keys=n.shemshchun.tolist())
dest_point_ins = dest_point_ins.idxmax(axis=1)
dest_point_ins
Out[44]:
0                (יפו ד' (גבעת התמרים
1            הצפון הישן - החלק הצפוני
2              הצפון הישן-החלק הדרומי
3                                בבלי
4              הצפון הישן-החלק הדרומי
5                            צפון יפו
6                             תל-חיים
7                               התקוה
8              הצפון הישן-החלק הדרומי
9                             נוה שרת
10                         לב תל-אביב
11                          רמת החייל
12                            נופי ים
13           הצפון הישן - החלק הצפוני
14             מכללת יפו תל אביב ודקר
15                  ביצרון ורמת ישראל
16             הצפון החדש-החלק הדרומי
17             הצפון החדש-החלק הדרומי
18                         לב תל-אביב
19        הצפון החדש-סביבת ככר המדינה
20        הצפון החדש-סביבת ככר המדינה
21                            נוה צדק
22           הצפון החדש - החלק הצפוני
23           הצפון החדש - החלק הצפוני
24                               בבלי
25                         לב תל-אביב
26                     אוניברסיטת ת"א
27                            נופי ים
28                             עתידים
29                         לב תל-אביב
                     ...             
136301         הצפון הישן-החלק הדרומי
136302                     לב תל-אביב
136303                       פלורנטין
136304               עג'מי וגבעת עליה
136305                     לב תל-אביב
136306                    'רמת אביב ג
136307                           צהלה
136308    הצפון החדש-סביבת ככר המדינה
136309       הצפון הישן - החלק הצפוני
136310                       פלורנטין
136311                     מונטיפיורי
136312       הצפון הישן - החלק הצפוני
136313                         גלילות
136314                     לב תל-אביב
136315         הצפון הישן-החלק הדרומי
136316         הצפון הישן-החלק הדרומי
136317                       צפון יפו
136318                    פארק הירקון
136319                    'רמת אביב ג
136320                   'נאות אפקה ב
136321       הצפון הישן - החלק הצפוני
136322                 אוניברסיטת ת"א
136323                        נוה צדק
136324                       פלורנטין
136325                       יד אליהו
136326    הצפון החדש-סביבת ככר המדינה
136327                        נוה צדק
136328                       פלורנטין
136329             נוה אביבים וסביבתה
136330               עג'מי וגבעת עליה
Length: 136331, dtype: object
In [45]:
df['orig_nei'] = orig_point_ins
df['dest_nei'] = dest_point_ins
In [46]:
#pickle.dump(df, open('df_with_neis.pkl', 'wb'))

Trips that start/end outside of Te-Aviv-Yafo municipal borders

In [47]:
df[df.orig_nei.isna()].shape[0]/df.shape[0]
Out[47]:
0.09147589323044648
In [48]:
df[df.dest_nei.isna()].shape[0]/df.shape[0]
Out[48]:
0.09147589323044648
In [49]:
df.groupby(['orig_nei', 'dest_nei']).size().sort_values(ascending=False)
Out[49]:
orig_nei                     dest_nei                   
לב תל-אביב                   לב תל-אביב                     2093
הצפון הישן - החלק הצפוני     לב תל-אביב                     1671
לב תל-אביב                   הצפון הישן - החלק הצפוני       1623
הצפון הישן - החלק הצפוני     הצפון הישן - החלק הצפוני       1571
הצפון הישן-החלק הדרומי       לב תל-אביב                     1417
לב תל-אביב                   הצפון הישן-החלק הדרומי         1209
הצפון הישן-החלק הדרומי       הצפון הישן-החלק הדרומי          993
                             הצפון הישן - החלק הצפוני        930
הצפון הישן - החלק הצפוני     הצפון הישן-החלק הדרומי          902
לב תל-אביב                   הצפון החדש-סביבת ככר המדינה     707
צפון יפו                     לב תל-אביב                      658
הצפון החדש-סביבת ככר המדינה  לב תל-אביב                      637
                             הצפון החדש-סביבת ככר המדינה     620
לב תל-אביב                   הצפון החדש-החלק הדרומי          611
הצפון החדש-החלק הדרומי       לב תל-אביב                      608
לב תל-אביב                   צפון יפו                        606
                             פלורנטין                        604
הצפון הישן - החלק הצפוני     הצפון החדש-סביבת ככר המדינה     584
הצפון החדש-סביבת ככר המדינה  הצפון הישן - החלק הצפוני        554
הצפון הישן-החלק הדרומי       הצפון החדש-סביבת ככר המדינה     526
פלורנטין                     לב תל-אביב                      506
נוה אביבים וסביבתה           הצפון הישן - החלק הצפוני        490
הצפון הישן - החלק הצפוני     הצפון החדש-החלק הדרומי          486
הצפון החדש-סביבת ככר המדינה  הצפון הישן-החלק הדרומי          475
הצפון החדש-החלק הדרומי       הצפון הישן - החלק הצפוני        454
לב תל-אביב                   הצפון החדש - החלק הצפוני        444
הצפון הישן - החלק הצפוני     הצפון החדש - החלק הצפוני        433
                             נוה אביבים וסביבתה              429
פלורנטין                     הצפון הישן-החלק הדרומי          416
הצפון הישן - החלק הצפוני     פלורנטין                        416
                                                            ... 
נוה דן                       נופי ים                           1
אזור שדה דב                  מרכז הירידים                      1
                             מעוז אביב                         1
פארק דרום                    הצפון הישן - החלק הצפוני          1
גני צהלה, רמות צהלה          צהלון ושיכוני חסכון               1
אזור שדה דב                  שפירא                             1
גני צהלה, רמות צהלה          צוקי אביב                         1
קרית שאול                    מרכז הירידים                      1
גני צהלה, רמות צהלה          קרית שאול                         1
קרית שאול                    נוה חן                            1
                             נוה צדק                           1
נוה שאנן                     אזור תעסוקה-צומת חולון            1
קרית שאול                    התקוה                             1
גני צהלה, רמות צהלה          פארק החורשות                      1
מרכז הירידים                 קרית שלום                         1
אזור שדה דב                  נוה שרת                           1
צהלה                         שפירא                             1
נוה שאנן                     'נאות אפקה ב                      1
אזור שדה דב                  ניר אביב                          1
מרכז הירידים                 קרית שאול                         1
אזור שדה דב                  פארק הירקון                       1
רמת הטייסים                  נוה אליעזר וכפר שלם מזרח          1
אזור שדה דב                  צהלה                              1
                             צהלון ושיכוני חסכון               1
                             צוקי אביב                         1
מכללת יפו תל אביב ודקר       אזור תעסוקה-צומת חולון            1
                             אפקה                              1
אזור שדה דב                  רמת החייל                         1
מכללת יפו תל אביב ודקר       גלילות                            1
צוקי אביב                    ביצרון ורמת ישראל                 1
Length: 3801, dtype: int64
In [50]:
orig_neis = df.orig_nei.value_counts().reset_index().rename(columns={'index': 'nei', 'orig_nei': 'trips'})
dest_neis = df.dest_nei.value_counts().reset_index().rename(columns={'index': 'nei', 'dest_nei': 'trips'})
In [51]:
import altair as alt
from altair.expr import datum

alt.renderers.enable('notebook')
alt.data_transformers.enable('json')
Out[51]:
DataTransformerRegistry.enable('json')
In [52]:
alt.Chart(orig_neis).mark_bar().encode(
    x=alt.X('nei:N', title='שכונת מוצא'),
    y=alt.Y('trips:Q', title='כמות נסיעות'),
    color=alt.Color('nei:N', legend=None),
)
Out[52]:

In [53]:
alt.Chart(dest_neis).mark_bar().encode(
    x=alt.X('nei:N', title='שכונת יעד'),
    y=alt.Y('trips:Q', title='כמות נסיעות'),
    color=alt.Color('nei:N', legend=None),
)
Out[53]:

In [54]:
counts = (pd.concat([df.orig_nei.value_counts(), df.dest_nei.value_counts()], axis=1, keys=['as_orig', 'as_dest'])
          .stack()
          .reset_index()
          .rename(columns={'level_0': 'nei', 'level_1': 'as_what', 0: 'trips'}))
In [55]:
counts
Out[55]:
nei as_what trips
0 'נאות אפקה א as_orig 1407
1 'נאות אפקה א as_dest 1380
2 'נאות אפקה ב as_orig 1065
3 'נאות אפקה ב as_dest 1073
4 'רמת אביב ג as_orig 2406
5 'רמת אביב ג as_dest 2446
6 'תכנית ל as_orig 1158
7 'תכנית ל as_dest 1193
8 (יפו ד' (גבעת התמרים as_orig 378
9 (יפו ד' (גבעת התמרים as_dest 380
10 אוניברסיטת ת"א as_orig 3085
11 אוניברסיטת ת"א as_dest 3065
12 אורות as_orig 104
13 אורות as_dest 122
14 אזור שדה דב as_orig 409
15 אזור שדה דב as_dest 962
16 אזור תעסוקה-צומת חולון as_orig 65
17 אזור תעסוקה-צומת חולון as_dest 60
18 אפקה as_orig 1177
19 אפקה as_dest 1208
20 בבלי as_orig 1566
21 בבלי as_dest 1570
22 ביצרון ורמת ישראל as_orig 2495
23 ביצרון ורמת ישראל as_dest 2583
24 גבעת הרצל, אזור המלאכה-יפו as_orig 1234
25 גבעת הרצל, אזור המלאכה-יפו as_dest 1250
26 גלילות as_orig 429
27 גלילות as_dest 520
28 גני צהלה, רמות צהלה as_orig 768
29 גני צהלה, רמות צהלה as_dest 797
... ... ... ...
112 צהלה as_orig 434
113 צהלה as_dest 439
114 צהלון ושיכוני חסכון as_orig 522
115 צהלון ושיכוני חסכון as_dest 558
116 צוקי אביב as_orig 1114
117 צוקי אביב as_dest 1095
118 צמרות איילון as_orig 1349
119 צמרות איילון as_dest 1326
120 צפון יפו as_orig 3613
121 צפון יפו as_dest 3744
122 קרית שאול as_orig 66
123 קרית שאול as_dest 89
124 קרית שלום as_orig 633
125 קרית שלום as_dest 651
126 רביבים as_orig 657
127 רביבים as_dest 621
128 רמת החייל as_orig 1310
129 רמת החייל as_dest 1322
130 רמת הטייסים as_orig 462
131 רמת הטייסים as_dest 469
132 רמת-אביב as_orig 2611
133 רמת-אביב as_dest 2587
134 שפירא as_orig 781
135 שפירא as_dest 792
136 תל ברוך as_orig 937
137 תל ברוך as_dest 984
138 תל ברוך צפון as_orig 472
139 תל ברוך צפון as_dest 486
140 תל-חיים as_orig 360
141 תל-חיים as_dest 379

142 rows × 3 columns

In [56]:
alt.Chart(counts.reset_index()).mark_bar().encode(
    x=alt.X('nei:N', title='שכונה'),
    y=alt.Y('trips:Q', title='כמות נסיעות'),
    color=alt.Color('as_what:N'),
)
Out[56]:

In [57]:
orig_dest_counts = df.groupby(['orig_nei', 'dest_nei']).size().to_frame().unstack().fillna(0)
In [58]:
orig_dest_counts.columns = orig_dest_counts.columns.get_level_values(1)
In [59]:
sorter = orig_dest_counts.sum().sort_values(ascending=False).index.tolist()

sorterIndex = dict(zip(sorter,range(len(sorter))))
In [60]:
orig_dest_counts = orig_dest_counts.reset_index()
orig_dest_counts['rank'] = orig_dest_counts['orig_nei'].map(sorterIndex)
orig_dest_counts = orig_dest_counts.set_index('orig_nei')
In [61]:
orig_dest_counts = orig_dest_counts[sorter+['rank']].sort_values(by='rank', ascending=False).drop('rank', 1)
In [62]:
orig_dest_counts.head()
Out[62]:
dest_nei לב תל-אביב הצפון הישן - החלק הצפוני הצפון הישן-החלק הדרומי הצפון החדש-סביבת ככר המדינה הצפון החדש-החלק הדרומי פלורנטין נוה אביבים וסביבתה צפון יפו הצפון החדש - החלק הצפוני אוניברסיטת ת"א ... נוה אליעזר וכפר שלם מזרח יפו ג' ונוה גולן ניר אביב אורות נוה ברבור, כפר שלם מערב קרית שאול פארק החורשות אזור תעסוקה-צומת חולון כפיר פארק דרום
orig_nei
פארק דרום 1.0 1.0 2.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
כפיר 9.0 1.0 12.0 0.0 6.0 3.0 0.0 0.0 0.0 0.0 ... 3.0 0.0 0.0 1.0 0.0 0.0 0.0 0.0 2.0 0.0
אזור תעסוקה-צומת חולון 17.0 6.0 6.0 3.0 4.0 3.0 0.0 1.0 0.0 2.0 ... 0.0 0.0 0.0 0.0 0.0 0.0 0.0 2.0 0.0 0.0
פארק החורשות 10.0 5.0 8.0 0.0 0.0 0.0 2.0 6.0 2.0 0.0 ... 0.0 0.0 0.0 0.0 1.0 0.0 1.0 0.0 0.0 0.0
קרית שאול 7.0 6.0 7.0 4.0 3.0 1.0 0.0 1.0 0.0 0.0 ... 0.0 0.0 0.0 0.0 0.0 4.0 0.0 0.0 0.0 0.0

5 rows × 71 columns

In [63]:
viz_df = orig_dest_counts.rename(columns={s:s[::-1] for s in  list(orig_dest_counts.columns)}, index = {s:s[::-1] for s in  list(orig_dest_counts.columns)})
In [64]:
fig, ax = plt.subplots(figsize=(30,30))
with sns.axes_style("white"):
    ax = sns.heatmap(viz_df, robust=True, square=True, cbar=False, ax=ax)

Distance

In [65]:
import requests, json

mapbox_access_token = 'pk.eyJ1IjoiY2plciIsImEiOiJjamhvM2pkMzEweWE2MzBzMW1sZTV0djY3In0.WbUMFEwp9GmgLP5NmCrkig'
url="https://api.mapbox.com/directions/v5/mapbox/driving/"

def get_distances(row):
    o1 = str(row['orig_x']) +',' + str(row['orig_y'])
    o2 = str(row['dest_x']) + ',' + str(row['dest_y']) 
    x = o1 + ';' + o2
    #print(x)
    response = requests.get(url+x+'?access_token='+mapbox_access_token)
    data = json.loads(response.content)
    
    if response.status_code == 200:
        return data['routes'][0]['distance']#, data['routes'][0]['duration'])
    else:
        return -1
df['mapbox_distance'] = df.apply(get_distances, reduce=True, axis=1)
In [66]:
#pickle.dump(df, open('data/df3.pkl', 'wb'))

Holoviews

In [83]:
import holoviews as hv, geoviews as gv, dask.dataframe as dd, cartopy.crs as crs
from colorcet import fire
from holoviews.operation.datashader import datashade

options = dict(width=600, height=600, xaxis=None, yaxis=None, bgcolor='black')
points = hv.Points(df, ['orig_x_merc', 'orig_y_merc'])
autotel_trips = datashade(points, x_sampling=0.8, y_sampling=0.8, cmap=fire).opts(plot=options)
url = 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{Z}/{Y}/{X}.jpg'
tiles = gv.WMTS(url, crs=crs.GOOGLE_MERCATOR)
tiles * autotel_trips
Out[83]:
In [81]:
import param, parambokeh
from colorcet import cm_n
from holoviews.streams import RangeXY

url_base='https://server.arcgisonline.com/ArcGIS/rest/services/'
url_suffix = '/MapServer/tile/{Z}/{Y}/{X}.jpg'
opts = dict(width=600,height=800,xaxis=None,yaxis=None,bgcolor='black',show_grid=False)

class AutotelExplorer(hv.streams.Stream):
    alpha      = param.Magnitude(default=0.35, doc="Alpha value for the map opacity")
    sampling   = param.Magnitude(default=0.21, doc="Sampling")
    colormap   = param.ObjectSelector(default=cm_n["fire"], objects=cm_n.values())
    location   = param.ObjectSelector(default='orig', objects=['orig', 'dest'])
    tile_type  = param.ObjectSelector(default='World_Street_Map', objects=['World_Imagery', 'World_Street_Map', 'World_Topo_Map', 'World_Physical_Map'])

    def make_view(self, x_range, y_range, **kwargs):
        tiles = gv.WMTS(url_base+self.tile_type+url_suffix,crs=crs.GOOGLE_MERCATOR)
        map_tiles = tiles.options(alpha=self.alpha, **opts)
        points = hv.Points(df, [self.location+'_x_merc', self.location+'_y_merc'])
        autotel_trips = datashade(points, x_sampling=(self.sampling**2)*1000, y_sampling=(self.sampling**2)*1000, cmap=self.colormap,
                               dynamic=False, x_range=x_range, y_range=y_range, width=600, height=800)
        return map_tiles * autotel_trips
In [82]:
explorer = AutotelExplorer(name="AutoTel Trips")
parambokeh.Widgets(explorer, callback=explorer.event)
hv.DynamicMap(explorer.make_view, streams=[explorer, RangeXY()])
Out[82]:

Times

In [70]:
df['start_hour'] = df['start_dt'].dt.hour
df['start_dayofweek'] = df['start_dt'].dt.strftime('%A')
df['start_month'] = df['start_dt'].dt.strftime('%B')
dfh = (df[(df.billing_minutes<100) & (df.avg_dist<100) & (df.kmh<50)]
       .set_index('start_dt')
       .resample('1H')
       .agg({'start_hour': 'min', 'start_dayofweek': 'min', 'start_month': 'min', 
             'order_id':'count', 'avg_dist': 'mean', 'billing_minutes': 'mean'}))
dfh.head()
Out[70]:
start_hour start_dayofweek start_month order_id avg_dist billing_minutes
start_dt
2017-10-23 00:00:00 0.0 Monday October 11 4.363636 10.507273
2017-10-23 01:00:00 1.0 Monday October 1 8.000000 13.920000
2017-10-23 02:00:00 2.0 Monday October 3 6.000000 10.366667
2017-10-23 03:00:00 NaN NaN NaN 0 NaN NaN
2017-10-23 04:00:00 NaN NaN NaN 0 NaN NaN
In [71]:
base = alt.Chart(dfh).mark_rect().encode(
    x = 'start_hour:O',
    y = alt.Y('start_dayofweek:N', sort=['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']),
    color='mean(order_id):Q',
    tooltip='mean(order_id):Q'
)

chart = alt.vconcat()
for month in df.start_month.unique():
    chart &= base.transform_filter(datum.start_month == month).properties(title=month)
chart
Out[71]:

In [72]:
base = alt.Chart(dfh).mark_rect().encode(
    x = 'start_hour:O',
    y = alt.Y('start_dayofweek:N', sort=['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']),
    color='mean(avg_dist):Q',
    tooltip='mean(avg_dist):Q'
)

chart = alt.vconcat()
for month in df.start_month.unique():
    chart &= base.transform_filter(datum.start_month == month).properties(title=month)
chart
Out[72]:

In [73]:
base = alt.Chart(dfh[dfh.billing_minutes<40]).mark_rect().encode(
    x = 'start_hour:O',
    y = alt.Y('start_dayofweek:N', sort=['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']),
    color=alt.Color('mean(billing_minutes):Q'),
    tooltip='mean(billing_minutes):Q'
)

chart = alt.vconcat()
for month in df.start_month.unique()[3:]:
    chart &= base.transform_filter(datum.start_month == month).properties(title=month)
chart
Out[73]:

In [74]:
base = alt.Chart(df[['start_dayofweek', 'start_month']]).mark_rect().encode(
    x=alt.X('start_dayofweek:N', sort=['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']),
    y='count()',
    color='start_dayofweek:N',
)
total = base.properties(title='Total')
chart = alt.vconcat()
for month in df.start_month.unique():
    chart |= base.transform_filter(datum.start_month == month).properties(title=month)
total | chart
Out[74]:

Vehicles

In [75]:
df.set_index('start_dt').resample('1D').vehicle_id.nunique().ewm(span=7).mean().plot()
Out[75]:
<matplotlib.axes._subplots.AxesSubplot at 0x2e8c0621198>
In [76]:
df.set_index('start_dt').groupby([pd.Grouper(freq='1D'), 'vehicle_id']).size().hist(bins=16)
Out[76]:
<matplotlib.axes._subplots.AxesSubplot at 0x2e8c08d07f0>
In [77]:
df.set_index('start_dt').groupby([pd.Grouper(freq='1D'), 'vehicle_id']).size().median()
Out[77]:
3.0
In [78]:
df.set_index('start_dt').groupby([pd.Grouper(freq='1D'), 'vehicle_id']).size().mean()
Out[78]:
3.6568493334406265
In [79]:
df[df.billing_minutes<200].set_index('start_dt').groupby([pd.Grouper(freq='1D'), 'vehicle_id']).billing_minutes.sum().mean()
Out[79]:
65.90044531144882
In [80]:
df[df.billing_minutes<200].set_index('start_dt').groupby([pd.Grouper(freq='1D'), 'vehicle_id']).billing_minutes.sum().median()
Out[80]:
55.75

Comments !